/****************************************************************************
 *
 *  ALi (Shanghai) Corporation, All Rights Reserved. 2007 Copyright (C)
 *
 *  File: music_engine.c
 *
 *  Description: This file describes music engine operations.
 *
 *  History:
 *      Date        	Author      	Version  		Comment
 *      ====        	======      =======  	=======
 *  1.  2007.5.22  Martin_Xia  	0.0.1
 *
 ****************************************************************************/

#include <types.h>

#include <api/libc/list.h>
#include <api/libc/alloc.h>
#include <api/libc/string.h>
#include <api/libmp/pe.h>
#include "plugin.h"
#include "music_engine.h"

/****************************Defines*****************************************/
#define ArraySize(p) (sizeof(p)/sizeof((p)[0]))



/**********************External  Variables***********************************/
extern InputPlugin mp3_input_plugin;
extern InputPlugin ogg_input_plugin;



/**********************Global  Variables************************************/
#define     MAX_AUDIO_PLUGIN_SUPPORT    8
InputPlugin**   intput_plugin_array;
static int   g_used_audio_plugin_count = 0;

/*[] =
{
	&mp3_input_plugin,
	&ogg_input_plugin
};
*/
input_plugin_data	ip_data;
static mp_callback_func func_mp_cb;
//static int seek_cnt=0;



/********************Function  Declaration**********************************/

void set_current_input_plugin(InputPlugin * ip);

InputPlugin *get_current_input_plugin(void);

BOOL get_input_playing(void);

BOOL get_input_paused(void);


/********************DEBUG LOG***********************************************/
#define DEBUG_MUSIC_ENGINE 1
#ifdef DEBUG_MUSIC_ENGINE
#define MUSIC_ENGINE_PRINTF   PRINTF
#else
#define MUSIC_ENGINE_PRINTF(...)	do{}while(0)
#endif



/////////////////////////////////////////////////////////////////////////////
// set_current_input_plugin
//
// Description:
//	Set ip_data.current_output_plugin to the specific value
//
// Arguments:
//	IN  InputPlugin *ip				The destinaton value
//
// Return Value:
//  None
//
/////////////////////////////////////////////////////////////////////////////

void set_current_input_plugin(InputPlugin * ip)
{
	ip_data.current_input_plugin = ip;
}

/////////////////////////////////////////////////////////////////////////////
// get_current_output_plugin
//
// Description:
//	Get ip_data.current_output_plugin value
//
// Arguments:
//	None
//
// Return Value:
//  ip_data.current_output_plugin
//
/////////////////////////////////////////////////////////////////////////////

InputPlugin *get_current_input_plugin(void)
{
	return ip_data.current_input_plugin;
}

/////////////////////////////////////////////////////////////////////////////
// get_input_playing
//
// Description:
//	Judge whether a song playing now or not
//
// Arguments:
//  None
//
// Return Value:
//  True			There is a song playing now
//  False			There is not a song playing now
//
/////////////////////////////////////////////////////////////////////////////

BOOL get_input_playing(void)
{
	return ip_data.playing;
}

/////////////////////////////////////////////////////////////////////////////
// get_input_paused
//
// Description:
//	Judge whether a song paused now or not
//
// Arguments:
//  None
//
// Return Value:
//  True			There is a song paused now
//  False			There is not a song paused now
//
/////////////////////////////////////////////////////////////////////////////

BOOL get_input_paused(void)
{
	return ip_data.paused;
}
UINT8 music_b_disableSeek = FALSE;
void music_engine_set_disableSeek(UINT8 disable)
{
    music_b_disableSeek = disable;
}

UINT8 music_engine_get_disableSeek()
{
    return music_b_disableSeek;
}

extern UINT32 _audio_init_struct_start;
extern UINT32 _audio_init_struct_end;

static void music_engine_install_plugin(void)
{
    intput_plugin_array = (InputPlugin**)MALLOC(MAX_AUDIO_PLUGIN_SUPPORT * sizeof(void*));
	MEMSET(intput_plugin_array, 0, MAX_AUDIO_PLUGIN_SUPPORT * sizeof(void*));
    g_used_audio_plugin_count = 0;
    UINT32 audio_init_struct_start = (UINT32)(&_audio_init_struct_start);
    UINT32 audio_init_struct_end = (UINT32)(&_audio_init_struct_end);

    InputPlugin * tmp = (InputPlugin*)(audio_init_struct_start);
    while( (UINT32)tmp < audio_init_struct_end )
    {
        intput_plugin_array[g_used_audio_plugin_count] = tmp;
        tmp ++;
        g_used_audio_plugin_count ++;
    }
}

/////////////////////////////////////////////////////////////////////////////
// music_engine_init
//
// Description:
//	Init music engine
//
// Arguments:
//	IN  mp_callback_func cb					Call back function passed from app.
//									It will be called when at the end of a song
//
// Return Value:
//  None
//
/////////////////////////////////////////////////////////////////////////////
#ifdef SEE_CPU
void hld_cpu_mus_cb(unsigned long type, unsigned long param);
#endif

int music_engine_init(struct pe_music_cfg *pe_music_cfg)
{
	UINT32				count = 0;

#ifdef SEE_CPU
    if(pe_music_cfg->mp_cb)
        func_mp_cb = hld_cpu_mus_cb;
    else
#endif
    {
        func_mp_cb = pe_music_cfg->mp_cb;
    }

    music_engine_install_plugin();

	//call init() of every plugin
	for (count = 0; count < (UINT32)g_used_audio_plugin_count; ++count)
	{
		(intput_plugin_array[count])->init(pe_music_cfg);
	}
    return 0;

}

/////////////////////////////////////////////////////////////////////////////
// music_engine_release
//
// Description:
//	Release music engine and its resources
//
// Arguments:
//	None
//
// Return Value:
//  None
//
/////////////////////////////////////////////////////////////////////////////

void music_engine_release()
{
	UINT32				count = 0;

	if (ip_data.playing)
	{
		music_engine_stop();
	}

	for (count = 0; count < (UINT32)g_used_audio_plugin_count; ++count)
	{
		if ((intput_plugin_array[count])->cleanup)
		{
			(intput_plugin_array[count])->cleanup();
		}
	}
	func_mp_cb = NULL;
}

/////////////////////////////////////////////////////////////////////////////
// music_engine_play
//
// Description:
//	Play a song
//
// Arguments:
//	IN   char *filename			The file name of a song
//
// Return Value:
//  -1					Unsuccessful
//   0					Success
//
/////////////////////////////////////////////////////////////////////////////

int music_engine_play(char *filename)
{
	UINT32				count = 0;
	InputPlugin 		*ip;
    int ret = 0;

	for (count = 0; count < (UINT32)g_used_audio_plugin_count; ++count)
	{
		ip = intput_plugin_array[count];
		if (ip)
            ret = ip->is_our_file(filename);
        else
		    break;
        if (ret == MUSIC_OPEN_FILE_FAIL)
            break;

        if (ret == MUSIC_IS_OUR_FILE)
		{
			set_current_input_plugin(ip);
			if (ip->play_file(filename, func_mp_cb) == 0)
			{
				ip_data.playing = TRUE;
				return 0;
			}
		}
	};
	/* We set the playing flag even if play failed or no inputplugin
	   recognizes the file. This way we are sure it will be skipped. */
	ip_data.playing = TRUE;
	set_current_input_plugin(NULL);
	return -1;
}

/////////////////////////////////////////////////////////////////////////////
// music_engine_seek
//
// Description:
//	Forward/Backward the song for specific seconds
//
// Arguments:
//	IN   int time				The seconds you want to forward/backward
//
// Return Value:
//  None
//
/////////////////////////////////////////////////////////////////////////////

void music_engine_seek(int time)
{
	if (ip_data.playing && (!ip_data.paused) && get_current_input_plugin())
	{
		get_current_input_plugin()->seek(time);
	}
}

/////////////////////////////////////////////////////////////////////////////
// music_engine_stop
//
// Description:
//	Stop to play the song
//
// Arguments:
//	None
//
// Return Value:
//  None
//
/////////////////////////////////////////////////////////////////////////////

void music_engine_stop(void)
{
	if ((get_input_paused()||get_input_playing()) && get_current_input_plugin())
	{
		ip_data.playing = FALSE;

		if (ip_data.paused)
		{
			music_engine_pause();
		}
		if (get_current_input_plugin()->stop)
		{
			get_current_input_plugin()->stop();
		}
		ip_data.paused = FALSE;

	}
	ip_data.playing = FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// music_engine_pause
//
// Description:
//	Pause/Remuse a song
//
// Arguments:
//	None
//
// Return Value:
//  None
//
/////////////////////////////////////////////////////////////////////////////

void music_engine_pause(void)
{
	if ((get_input_paused()||get_input_playing()) && get_current_input_plugin())
	{
		ip_data.paused = !ip_data.paused;

		get_current_input_plugin()->pause(ip_data.paused);
	}
}

/////////////////////////////////////////////////////////////////////////////
// music_engine_play
//
// Description:
//	Get current playing time
//
// Arguments:
//	None
//
// Return Value:
//  -1					Unsuccessful
//  time				Current playing time
//
/////////////////////////////////////////////////////////////////////////////

int music_engine_get_time(void)
{
	int time;

	if (get_input_playing() && get_current_input_plugin())
	{
		time = get_current_input_plugin()->get_time();
	}
	else
	{
		time = -1;
	}

	return time;
}

/////////////////////////////////////////////////////////////////////////////
// music_engine_set_eq
//
// Description:
//	Set the equalizer
//
// Arguments:
//	TBD
//
// Return Value:
//  None
//
/////////////////////////////////////////////////////////////////////////////

void music_engine_set_eq(int on, float preamp, float *bands)
{
	if (ip_data.playing)
	{
		if (get_current_input_plugin() && get_current_input_plugin()->set_eq)
		{
			get_current_input_plugin()->set_eq(on, preamp, bands);
		}
	}
}

/////////////////////////////////////////////////////////////////////////////
// music_engine_get_song_info
//
// Description:
//	Get current song Information
//
// Arguments:
//  IN  char *filename						The file name of the song
//  OUT MusicInfo *music_info				The struct of song infomation
//
// Return Value:
//  -1					Unsuccessful
//   1					Music infomation valid
//   2					Playing time valid
//   3					All valid
//
/////////////////////////////////////////////////////////////////////////////

int music_engine_get_song_info(char * filename, MusicInfo *music_info)
{
	InputPlugin 						*ip = NULL;
	UINT32								count = 0;
    int ret = 0;
    UINT8 url_file_flag = 0;

    if((!MEMCMP(filename,"http://",7))||(!MEMCMP(filename,"/dmpfs/dlna/",12)))
    {
        url_file_flag = 1;
    }
    else
    {
        url_file_flag = 0;
    }
    if(url_file_flag)//1 == music_b_disableSeek)
    {
        if (get_current_input_plugin() /*&& get_current_input_plugin()->set_info && (1 == music_b_disableSeek)*/)
        {
            MUSIC_ENGINE_PRINTF("in mode /~play~/\n");
            get_current_input_plugin()->get_song_info(filename, music_info);
            ret = 0;
        }
        else
        {
            MUSIC_ENGINE_PRINTF("in mode /~play~/\n");
            ret = -1;
        }

        return ret;
    }

	for (count = 0; count < (UINT32)g_used_audio_plugin_count; ++count)
	{
		ip = intput_plugin_array[count];
		if (ip )
            ret = ip->is_our_file(filename);
        else
			break;
        if (ret == MUSIC_OPEN_FILE_FAIL)
            break;

        if (ret == MUSIC_IS_OUR_FILE)
		{
			if (ip->get_song_info)
			{
				return ip->get_song_info(filename, music_info);
			}
		}

	}

	return -1;
}

/////////////////////////////////////////////////////////////////////////////
// music_engine_get_decoder_info
//
// Description:
//	To get the infomation that comes from the decoder of the current playing song
//
// Arguments:
//	None
//
// Return Value:
//  -1					Unsuccessful
//   0					Success
//
/////////////////////////////////////////////////////////////////////////////

int music_engine_get_decoder_info(char * filename, DecoderInfo *decoder_info)
{
	InputPlugin 						*ip = NULL;
	UINT32								count = 0;
    int ret = 0;

	if (ip_data.playing)
	{
		if (get_current_input_plugin() && get_current_input_plugin()->get_decoder_info)
		{
			return get_current_input_plugin()->get_decoder_info(filename, decoder_info);
		}
	}

    // the interface may use for set music config. so it can set before playing.
    if (decoder_info->bit_rate && decoder_info->channel_mode && decoder_info->sample_rate)
    {
    	for (count = 0; count < (UINT32)g_used_audio_plugin_count; ++count)
    	{
    		ip = intput_plugin_array[count];
    		if (ip )
                ret = ip->is_our_file(filename);
			else
				break;
            if (ret == MUSIC_OPEN_FILE_FAIL)
                break;
            if (ret == MUSIC_IS_OUR_FILE)
    		{
    			if (ip->get_decoder_info)
    			{
    				return ip->get_decoder_info(filename, decoder_info);
    			}
    		}
    	}
    }

	return -1;
}
/////////////////////////////////////////////////////////////////////////////
// music_engine_set_other_info
//
// Description:
//	To set the infomation that comes from the network
//
// Arguments:
//	None
//
// Return Value:
//  -1					Unsuccessful
//   0					Success
//
/////////////////////////////////////////////////////////////////////////////
int music_engine_set_other_info(UINT32 cmd, UINT32 param1)
{
    int ret = 0;
    MusicInfo *other_info = NULL;

    other_info = (MusicInfo *)MALLOC(sizeof(MusicInfo));
    if(NULL == other_info)
    {
        MUSIC_ENGINE_PRINTF("malloc other_info fail\n");
        return -1;
    }

	if (ip_data.playing)
	{
        switch(cmd)
        {
        case MP_SET_NETWORK_OTHER_INFO:
            if (get_current_input_plugin() && get_current_input_plugin()->set_info && (1 == music_b_disableSeek))
            {
                MUSIC_ENGINE_PRINTF("in mode /~play~/\n");
                MEMCPY(other_info,(MusicInfo *)param1,sizeof(MusicInfo));
                MUSIC_ENGINE_PRINTF("file_length:%d\n",other_info->file_length);
                get_current_input_plugin()->set_info(other_info->title, other_info->file_length,
                                other_info->bitrate, other_info->samplerate, other_info->channel_number);
                ret = 0;
            }
            else
            {
                MUSIC_ENGINE_PRINTF("in mode /~idle~/\n");
                ret = -1;
            }
            break;
        default:
            ret = -1;
            break;
        }
	}

    if(NULL != other_info)
    {
        FREE(other_info);
        MUSIC_ENGINE_PRINTF("FREE other_info success\n");
    }
    else
    {
        MUSIC_ENGINE_PRINTF("no need to FREE\n");;
    }
    return ret;


}

